/*
	Copyright (c) 2007, ANYwebcam.com Pty Ltd. All rights reserved.

	The software in this package is published under the terms of the BSD style 
	license, a copy of which has been included with this distribution in the 
	license.txt file.
*/
package com.anywebcam.reflection
{
	import com.anywebcam.reflection.ClassInfo;
	import com.anywebcam.reflection.MethodInfo;

	import flexunit.framework.TestCase;
	import flexunit.framework.TestSuite;

	public class ClassInfoTest extends TestCase
	{
		public static function suite():TestSuite
		{
			var ts:TestSuite = new TestSuite();

			var tests:Array = 
			[
				'toStringShouldReturnQualifiedClassName',
				'shouldReturnTypeForClass',
				'shouldReturnDescriptionForClass',
				'shouldReturnQualifiedName',
				'shouldReturnShortName',
				'shouldReturnPackageName',
				'shouldReturnInterfaces',
				'shouldReturnSuperClasses',
				'shouldCheckIfClassIsConcrete',
				'shouldCheckIfClassIsInterface',
				
				// disabled due to the way describeType works (or rather doesnt)
				//'shouldCheckIfClassIsDynamic',
				
				'shouldReturnConstructorMethodInfo',
				'shouldReturnMethodInfoForMethods',
				'shouldReturnMethodInfoForPropertiesAndVariables',
				'findGetterShouldReturnMethodInfoForReadablePropertyName',
				'findGetterShouldReturnMethodInfoForMethodWithNonVoidReturnValue',
				'findSetterShouldReturnMethodInfoForWritablePropertyName',
				'findSetterShouldReturnMethodInfoForSettableMethodName',
				'findGetterShouldNotReturnMethodInfoIfNoMatchingGetterFound',
				'findSetterShouldNotReturnMethodInfoIfNoMatchingSetterFound'
			];

			tests.forEach( function( methodName:String, index:int, array:Array ):void
			{
				ts.addTest( new ClassInfoTest( methodName ) );
			});

			return ts;
		}

		public function ClassInfoTest( method:String )
		{
			super( method );
		}
		
		public function toStringShouldReturnQualifiedClassName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertEquals( 'Expecting qualified class name in toString()', '[ClassInfo com.anywebcam.reflection::ClassInfo]', classInfo.toString() );
		}
		
		public function shouldReturnTypeForClass():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertEquals( 'Expecting the same Class', classInfo.type, ClassInfo );
		}
		
		public function shouldReturnDescriptionForClass():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertNotNull( 'Expecting description', classInfo.description ); 
			assertTrue( 'Expecting XML for description', classInfo.description is XML );
		}
		
		public function shouldReturnQualifiedName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertEquals( 'Expecting identical qualified names', 'com.anywebcam.reflection::ClassInfo', classInfo.qualifiedName );
		}
		
		public function shouldReturnShortName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertEquals( 'Expecting identical short names', 'ClassInfo', classInfo.shortName );
		}
		
		public function shouldReturnPackageName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertEquals( 'Expecting identical package names', 'com.anywebcam.reflection', classInfo.packageName );
		}

		public function shouldReturnInterfaces():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertNotNull( 'Expecting an empty array', classInfo.interfaces );
		}
		
		public function shouldReturnSuperClasses():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertNotNull( 'Expecting an empty array', classInfo.superClasses );
		}
		
		public function shouldCheckIfClassIsConcrete():void
		{
			var classInfo:ClassInfo = new ClassInfo( ConcreteClass );
			
			assertTrue( 'Expecting ConcreteClass to be concrete', classInfo.isConcrete );
			assertFalse( 'Expecting ConcreteClass to not be an Interface', classInfo.isInterface );
		}
		
		public function shouldCheckIfClassIsInterface():void
		{
			var classInfo:ClassInfo = new ClassInfo( InterfaceOnly );
			
			assertFalse( 'Expecting InterfaceOnly to not be concrete', classInfo.isConcrete );
			assertTrue( 'Expecting InterfaceOnly to be an Interface', classInfo.isInterface );
		}
		
		public function shouldCheckIfClassIsDynamic():void
		{
			var dynamicClassInfo:ClassInfo = new ClassInfo( DynamicClass );
			assertTrue( 'Expecting DynamicClass to be dynamic', dynamicClassInfo.isDynamic );

			var sealedClassInfo:ClassInfo = new ClassInfo( SealedClass );
			assertFalse( 'Expecting SealedClass not be dynamic', sealedClassInfo.isDynamic );
		}
		
		public function shouldReturnConstructorMethodInfo():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			var constructorInfo:MethodInfo = classInfo.constructor;
			
			assertNotNull( 'Expecting MethodInfo for constructor', constructorInfo );
			assertEquals( 'Expecting 1 parameter' , 1, constructorInfo.parameters.length );
			assertEquals( 'Expecting Parameter type to be Object' , Object, constructorInfo.parameters[0] );
		}
		
		public function shouldReturnMethodInfoForMethods():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertNotNull( 'Expecting an Array of MethodInfo instances', classInfo.methods );
			assertTrue( 'Expecting more then 0 methods', classInfo.methods.length > 0 );
			for each( var methodInfo:MethodInfo in classInfo.methods )
			{
				assertTrue( 'Expecting MethodInfo instance', methodInfo is MethodInfo );
			}
		}
		
		public function shouldReturnMethodInfoForPropertiesAndVariables():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassInfo );
			
			assertNotNull( 'Expecting an Array of MethodInfo instances', classInfo.properties );
			assertTrue( 'Expecting more then 0 properties', classInfo.properties.length > 0 );
			for each( var methodInfo:MethodInfo in classInfo.properties )
			{
				assertTrue( 'Expecting MethodInfo instance', methodInfo is MethodInfo );
			}
		}
		
		public function findGetterShouldReturnMethodInfoForReadablePropertyName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassWithReadableProperty );
			var methodInfo:MethodInfo = classInfo.getter( 'readableProperty' );
			assertNotNull( 'Expecting ClassInfo::findGetter to return a MethodInfo for ClassWithReableProperty', methodInfo );
			assertEquals( 'Expecting methodInfo.name to match requested findGetter parameter', 'readableProperty', methodInfo.name );
		}
		
		public function findGetterShouldNotReturnMethodInfoIfNoMatchingGetterFound():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassWithWritableProperty );
			var getter:MethodInfo = classInfo.setter( 'readableProperty' );
			
			assertNull( 'Expecting getter to be null', getter );
		}
		
		public function findGetterShouldReturnMethodInfoForMethodWithNonVoidReturnValue():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassWithGetPrefixedPropertyGetter );
			var getter:MethodInfo = classInfo.getter( 'propertyName' );
			
			assertNotNull( 'Expecting getter to not be null', getter );
		}
		
		public function findSetterShouldReturnMethodInfoForWritablePropertyName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassWithWritableProperty );
			var setter:MethodInfo = classInfo.setter( 'writableProperty' );
			
			assertNotNull( 'Expecting setter to not be null', setter );
		}
		
		public function findSetterShouldNotReturnMethodInfoIfNoMatchingSetterFound():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassWithReadableProperty );
			var setter:MethodInfo = classInfo.setter( 'writableProperty' );
			
			assertNull( 'Expecting setter to be null', setter );
		}
		
		public function findSetterShouldReturnMethodInfoForSettableMethodName():void
		{
			var classInfo:ClassInfo = new ClassInfo( ClassWithSetterMethod );
			var setter:MethodInfo = classInfo.setter( 'settableMethod' );
			
			assertNotNull( 'Expecting setter to not be null', setter );
		}		
	}
}

internal class ConcreteClass
{
	public function ConcreteClass() {}
}

internal interface InterfaceOnly
{
	function one():void
	function two( value:Object ):Boolean;
}

dynamic internal class DynamicClass
{
	public function DynamicClass() {}
}

internal class SealedClass
{
	public function SealedClass() {}
}

internal class ClassWithReadableProperty
{
	public function ClassWithReadableProperty() 		{}
	public function get readableProperty():String 	{ return 'readableProperty'; }
}

internal class ClassWithoutReaadbleProperty
{
	public function ClassWithoutReaadbleProperty() 	{}
	public function set unreadableProperty( value:String ):void {}
}

internal class ClassWithSetPrefixedPropertySetter
{
	public function ClassWithSetPrefixedPropertySetter() {}
	public function set setPropertyName( value:String ):void {}
}

internal class ClassWithGetPrefixedPropertyGetter
{
	public function ClassWithGetPrefixedPropertyGetter() {}
	public function get getPropertyName():String 	{ return 'propertyGetter'; } 
}

internal class ClassWithWritableProperty
{
	public function ClassWithWritableProperty() {}
	public function set writableProperty( value:String ):void {}
}

internal class ClassWithoutWritableProperty
{
	public function ClassWithoutWritableProperty() {}
	public function get unwritableProperty():String { return 'unwritableProperty'; }
}

internal class ClassWithSetterMethod
{
	public function ClassWithSetterMethod() {}
	public function settableMethod( value:String ):void {}
}

internal class ClassWithoutSettableMethod
{
	public function ClassWithoutSettableMethod() {}
	public function setUnsettable():void {}
}

internal class ClassWithSetPrefixedSetterMethod
{
	public function ClassWithSetPrefixedSetterMethod() {}
	public function setPrefixedSetter( value:String ):void {}
}

internal class ClassWithGetPrefixedGetterMethod
{
	public function ClassWithGetPrefixedGetterMethod() {}
	public function getPrefixedGetter():String { return 'getPrefixedGetter'; }
}